package com.recipedb.sprint.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.recipedb.sprint.entity.Users;
import com.recipedb.sprint.repository.UsersRepository;
import com.recipedb.sprint.service.UsersService;
import com.recipedb.sprint.security.JwtUtil;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Random;

@Service
public class UsersServiceImpl implements UsersService {

    private final UsersRepository repository;

    private final BCryptPasswordEncoder passwordEncoder;//小侯
    private final ObjectMapper objectMapper;

    // 构造器注入（顺序对应参数）
    public UsersServiceImpl(
            UsersRepository repository,
            BCryptPasswordEncoder passwordEncoder,
            ObjectMapper objectMapper) {
        this.repository = repository;
        this.passwordEncoder = passwordEncoder;
        this.objectMapper = objectMapper;
    }

    // 模拟验证码缓存（实际项目可用Redis，这里复用HashMap）
    private final Map<String, String> codeCache = new HashMap<>();

    /**
     * 用户注册
     */
    @Override
    public Map<String, Object> register(
            String username,
            String phone,
            String password,
            Map<String, Object> profile) {

        // 1. 校验手机号是否已注册
        Optional<Users> existingUser = repository.findByPhone(phone);
        if (existingUser.isPresent()) {
            throw new RuntimeException("手机号已注册");
        }

        // 2. 密码加密
        String encodedPassword = passwordEncoder.encode(password);

        // 3. 个人偏好（profile）转为JSON字符串
        String preferencesJson = null;
        try {
            if (profile != null && !profile.isEmpty()) {
                preferencesJson = objectMapper.writeValueAsString(profile);
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException("个人偏好格式错误: " + e.getMessage());
        }

        // 4. 自定义插入
        LocalDateTime now = LocalDateTime.now();
        repository.saveWithJsonb(
                now,                // createdAt
                encodedPassword,    // password
                phone,              // phone
                preferencesJson,    // preferences（JSON字符串）
                now,                // updatedAt
                username            // username
        );


        // 5. 查询刚插入的用户
        Users savedUser = repository.findByPhone(phone)
                .orElseThrow(() -> new RuntimeException("注册失败，用户未保存"));


        // 5. 生成Token（用TokenUtil替代JwtUtil）
        String token = JwtUtil.generateToken(savedUser.getUsername());
        //String token = JwtUtil.generateToken(user.getUsername());
        Map<String, Object> result = new HashMap<>();
        result.put("userid", savedUser.getUserid());
        result.put("username", savedUser.getUsername());
        result.put("token", token);
        return result;
    }


    /**
     * U-02：用户登录
     */
    @Override
    public Map<String, Object> login(String account, String password) {
        // 1. 按手机号查询，查不到则按用户名查询
        Users user = repository.findByPhone(account)
                .orElseGet(() -> repository.findByUsername(account)
                        .orElseThrow(() -> new RuntimeException("账号或密码错误")));

        // 2. 校验密码
        if (!passwordEncoder.matches(password, user.getPassword())) {
            throw new RuntimeException("账号或密码错误");
        }

        // 3. 生成Token
        String token = JwtUtil.generateToken(user.getUsername());
        Map<String, Object> result = new HashMap<>();
        result.put("userid", user.getUserid());
        result.put("username", user.getUsername());
        result.put("token", token);
        return result;
    }

    /**
     * U-03：查看个人资料
     */
    @Override
    public Map<String, Object> getProfile(Long userId) {
        // 1. 查询用户
        Users user = repository.findById(userId)
                .orElseThrow(() -> new RuntimeException("用户不存在"));

        // 2. 手机号脱敏
        String maskedPhone = user.getPhone() != null
                ? user.getPhone().replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2")
                : null;

        // 3. 解析偏好JSON为Map
        Map<String, Object> profile = new HashMap<>();
        try {
            if (user.getPreferences() != null) {
                profile = objectMapper.readValue(user.getPreferences(), Map.class);
            }
        } catch (JsonProcessingException e) {
            // 解析失败返回空Map
        }

        // 4. 组装结果
        Map<String, Object> result = new HashMap<>();
        result.put("username", user.getUsername());
        result.put("phone", maskedPhone);
        result.put("profile", profile);
        result.put("createdAt", user.getCreatedAt());
        return result;
    }



    /**
     * U-04：发送验证码（忘记密码用）
     */
    @Override
    public Map<String, Object> sendVerifyCode(String phone) {
        // 1. 校验手机号是否已注册
        if (repository.findByPhone(phone).isEmpty()) {
            throw new RuntimeException("手机号未注册");
        }

        // 2. 生成6位验证码
        //String code = String.format("%06d", new Random().nextInt(999999));
        String code = "123456";

        // 3. 缓存验证码（实际项目可用Redis，这里暂用HashMap，有效期5分钟）
        codeCache.put(phone, code);
        // 模拟过期：5分钟后清除（实际用Redis设置过期时间）
        new Thread(() -> {
            try {
                Thread.sleep(300000);
                codeCache.remove(phone);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();

        // 4. 模拟发送短信（实际调用短信API）
        System.out.println("验证码已发送至" + phone + "，验证码：" + code + "（5分钟内有效）");
        Map<String, Object> result = new HashMap<>();
        result.put("verifyCode", code);
        result.put("msg", "验证码已发送（测试环境直接返回，生产环境会发短信）");
        return result;
    }

    /**
     * U-04：重置密码
     */
    @Override
    public void resetPassword(String phone, String verifyCode, String newPassword) {
        // 1. 校验验证码
        String cachedCode = codeCache.get(phone);
        if (cachedCode == null || !cachedCode.equals(verifyCode)) {
            throw new RuntimeException("验证码错误或已过期");
        }

        // 2. 查询用户并更新密码
        Users user = repository.findByPhone(phone)
                .orElseThrow(() -> new RuntimeException("用户不存在"));
        user.setPassword(passwordEncoder.encode(newPassword));
        user.setUpdatedAt(LocalDateTime.now());
        repository.save(user);

        // 3. 清除验证码（防止重复使用）
        codeCache.remove(phone);
    }

    /**
     * 辅助方法：通过token获取用户
     */
    @Override
    public Users getUserByToken(String token) {
        System.out.println("接收到的Token: " + token);

        // 用TokenUtil解析token获取username
        String username = JwtUtil.validateToken(token);
        System.out.println("解析出的用户名：" + username);

        if (username == null) {
            System.out.println("Token验证失败，返回null");
            throw new RuntimeException("Token无效或已过期");
        }

        // 查询用户
        Optional<Users> userOptional = repository.findByUsername(username);
        System.out.println("用户查询结果是否存在: " + userOptional.isPresent());

        return userOptional
                .orElseThrow(() -> new RuntimeException("用户不存在: " + username));
    }
    /**
     * 更新用户信息（可部分更新）
     *
     * @param userId 用户ID
     * @param updates 前端传入的更新字段 Map
     *                支持 keys: username, preferences (Map<String,Object>)
     *                未传字段保持原值
     * @return 更新后的用户信息 Map
     */
    @Override
    public Map<String, Object> updateUser(Long userId, Map<String, Object> updates) {
        // 1. 查询用户（确保用户存在）
        Users user = repository.findById(userId)
                .orElseThrow(() -> new RuntimeException("用户不存在"));

        // 2. 处理用户名（如果有更新）
        String newUsername = user.getUsername(); // 默认用原用户名
        if (updates.containsKey("username") && updates.get("username") != null) {
            newUsername = updates.get("username").toString().trim();
            if (newUsername.isEmpty()) {
                throw new RuntimeException("用户名不能为空");
            }
        }

        // 3. 处理 preferences（合并旧数据 + 转为 JSON 字符串）
        String preferencesJson = user.getPreferences(); // 默认用原 preferences
        if (updates.containsKey("preferences") && updates.get("preferences") != null) {
            // 3.1 解析新传入的 preferences（前端传的 JSON 对象）
            Map<String, Object> newPreferences = (Map<String, Object>) updates.get("preferences");
            // 3.2 合并旧的 preferences（如果存在）
            Map<String, Object> mergedPreferences = new HashMap<>();
            if (user.getPreferences() != null) {
                try {
                    // 把数据库里的 JSON 字符串转成 Map
                    mergedPreferences = objectMapper.readValue(user.getPreferences(), Map.class);
                } catch (JsonProcessingException e) {
                    // 旧数据解析失败，直接用新数据
                }
            }
            mergedPreferences.putAll(newPreferences); // 新数据覆盖旧数据

            // 3.3 转为 JSON 字符串（用于存入数据库）
            try {
                preferencesJson = objectMapper.writeValueAsString(mergedPreferences);
            } catch (JsonProcessingException e) {
                throw new RuntimeException("偏好设置格式错误，无法转换为JSON");
            }
        }

        // 4. 调用自定义更新方法（带 jsonb 转换）
        LocalDateTime now = LocalDateTime.now();
        repository.updateUserWithJsonb(userId, newUsername, preferencesJson, now);

        // 5. 查询更新后的用户信息（用于返回结果）
        Users updatedUser = repository.findById(userId)
                .orElseThrow(() -> new RuntimeException("更新后查询用户失败"));

        // 6. 组装返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("userid", updatedUser.getUserid());
        result.put("username", updatedUser.getUsername());
        // 把数据库的 JSON 字符串转回 Map 给前端
        try {
            if (updatedUser.getPreferences() != null) {
                result.put("preferences", objectMapper.readValue(updatedUser.getPreferences(), Map.class));
            } else {
                result.put("preferences", new HashMap<>());
            }
        } catch (JsonProcessingException e) {
            result.put("preferences", new HashMap<>());
        }

        return result;
    }


    // TODO: 实现接口方法
}
